#ifndef FASTNET_TREE_HH
#define FASTNET_TREE_HH

#include <set>
#include <list>
#include <vector>
#include <memory>
#include <array>
#include <map>
#include "connection.hh"
#include "node.hh"

namespace cppn {
    class CPPN;
}

namespace brain {

    class Tree {
        cppn::CPPN* cppn;

        struct Leaf;
        std::map<Node, std::set<Connection>> outgoing_connections;
        std::map<Node, std::set<Connection>> incoming_connections;

    public:
        Tree (cppn::CPPN* cppn) : cppn(cppn) { }

        std::set<Connection> const& get_connections_for (Node const& node, bool outgoing);
        std::set<Connection> const& get_existing_connections_for (Node const& node) {
            if (outgoing_connections.count(node) == 1) return outgoing_connections.find(node)->second;
            else if (incoming_connections.count(node) == 1) return incoming_connections.find(node)->second;
            static std::set<Connection> empty{};
            return empty;
        }

    private:
        void prune(Node const& node, Leaf* leaf, bool outgoing, std::set<Connection>&); 
        float get_weight (float x1, float y1, float x2, float y2);
        float get_weight (Node const& src, Node const& dst);
        float get_weight (Node const& src, float x, float y);
        float get_weight (float x, float y, Node const& dst);

        static void get_leaf_values (Leaf* leaf, std::vector<float>& tmp);
        static float variance (Leaf* leaf);    
    };


    template<typename T, typename ...Args>
    std::unique_ptr<T> make_unique( Args&& ...args )
    {
        return std::unique_ptr<T>( new T( std::forward<Args>(args)... ) );
    }
}

#endif
